/************************************************************************
Copyright (c) 2010, 2013, C.R. All rights reserved. 

E-mail: crtoyou@gmail.com; crtoyou@163.com

You can redistribute it and/or modify it under the terms of the
GNU Lesser General Public License as published by the
Free Software Foundation version 3 of the License.

This source code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. if not, write to the Free Software Foundation, Inc., 
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
************************************************************************/

#ifndef __CR_EVENT_SOCKET_H__
#define __CR_EVENT_SOCKET_H__

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#include <mswsock.h>

#include <map>
#include <crdebug.h>
#include <crdefine.h>
#include <crthread.h>
#include <crpacket.h>

#include <import/sigslot.h>

//   New   WSAIoctl   Options  

#define SIO_RCVALL               _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST         _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST     _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS       _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT      _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF             _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS     _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND           _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF        _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST      _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST      _WSAIOW(IOC_VENDOR,11)

namespace cr {

/*Argument structure for SIO_KEEPALIVE_VALS*/  
struct tcp_keepalive
{
	u_long onoff;
	u_long keepalivetime;
	u_long keepaliveinterval;
};

struct __init_sock__ : WSADATA
{
	__init_sock__(WORD ver = 0x0202) { WSAStartup(ver, this); }
	~__init_sock__() { WSACleanup(); }
};

class CBaseSocket
{
private:
	CBaseSocket(const CBaseSocket&);
	CBaseSocket& operator=(const CBaseSocket&);

public:
	CBaseSocket()
		: m_hSocket(INVALID_SOCKET)
	{}

	virtual ~CBaseSocket()
	{
		Close();
	}

	inline SOCKET SafeHandle() const 
	{
		ASSERT(INVALID_SOCKET != m_hSocket);
		return m_hSocket;
	}

	virtual int Attach(SOCKET hSocket, long lEvent =
		FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE)
	{
		ASSERT(INVALID_SOCKET == m_hSocket);
		m_hSocket = hSocket;
		return 0;
	}

	virtual SOCKET Detach()
	{
		ASSERT(INVALID_SOCKET != m_hSocket);
		SOCKET hSocket = m_hSocket;
		m_hSocket = INVALID_SOCKET;
		return hSocket;
	}

	virtual int Create(const char* SocketIp = NULL, unsigned short nSocketPort = 0, 
		int nSocketType = SOCK_STREAM, 
		long lEvent = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE)
	{
		sockaddr_in sin;
		sin.sin_family = AF_INET;
		sin.sin_port = htons(nSocketPort);
		if (!SocketIp)
			SocketIp = "";
		long ip = inet_addr(SocketIp);
		if (INADDR_NONE == ip)
			ip = ADDR_ANY;
		sin.sin_addr.s_addr = ip;
		return Create((sockaddr*)&sin, sizeof(sin), nSocketType, lEvent);
	}

	virtual int Create(const sockaddr* sa, int salen, int nSocketType = SOCK_STREAM,
		long lEvent = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE)
	{
		int protocal = ((nSocketType==SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_IP);
		SOCKET hSocket = socket(AF_INET, nSocketType, protocal);
		if (INVALID_SOCKET == hSocket)
			return WSAGetLastError();
		TRACE(_T("socket 0x%X bind to %d.%d.%d.%d:%d\n"), hSocket,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b1,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b2,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b3,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b4,
			ntohs(((sockaddr_in*)sa)->sin_port));
		int ret = bind(hSocket, sa, salen);
		if (SOCKET_ERROR == ret)
			return WSAGetLastError();
		return Attach(hSocket);
	}

	virtual void Close()
	{
		if (INVALID_SOCKET != m_hSocket)
			closesocket(Detach());
	}

	int Connect(const char* RemoteIp, unsigned short RemotePort)
	{
		sockaddr_in sin;
		sin.sin_family = AF_INET;
		sin.sin_port = htons(RemotePort);
		sin.sin_addr.s_addr = inet_addr(RemoteIp);
		return Connect((sockaddr*)&sin, sizeof(sin));
	}

	int Connect(const sockaddr* sa, int salen)
	{
		if (INVALID_SOCKET == m_hSocket)
			return -1;
		TRACE(_T("socket 0x%X connect to %d.%d.%d.%d:%d\n"), m_hSocket,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b1,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b2,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b3,
			((sockaddr_in*)sa)->sin_addr.S_un.S_un_b.s_b4,
			ntohs(((sockaddr_in*)sa)->sin_port));
		if (SOCKET_ERROR == connect(m_hSocket, sa, salen))
			return WSAGetLastError();
		return 0;
	}

	int Listen(int backlog = 5)
	{
		TRACE(_T("socket 0x%X listen\n"), m_hSocket);
		if (SOCKET_ERROR == listen(m_hSocket, backlog))
			return WSAGetLastError();
		return 0;
	}

	int Accept(CBaseSocket& newSock, sockaddr* addr = NULL, int* addlen = NULL)
	{
		newSock.Close();
		SOCKET s = accept(m_hSocket, addr, addlen);
		TRACE(_T("socket:0x%X accept socket:0x%X at %d.%d.%d.%d:%d\n"), m_hSocket, s,
			((sockaddr_in*)addr)->sin_addr.S_un.S_un_b.s_b1,
			((sockaddr_in*)addr)->sin_addr.S_un.S_un_b.s_b2,
			((sockaddr_in*)addr)->sin_addr.S_un.S_un_b.s_b3,
			((sockaddr_in*)addr)->sin_addr.S_un.S_un_b.s_b4,
			ntohs(((sockaddr_in*)addr)->sin_port));
		return newSock.Attach(s);
	}

	int Send(const void* buf, int len, bool block = true)
	{
		if (!block)
			return send(m_hSocket, reinterpret_cast<const char*>(buf), len, 0);
		int pos = 0;
		while (pos < len)
		{
			int ret = send(m_hSocket, reinterpret_cast<const char*>(buf) + pos, len - pos, 0);
			if (ret > 0)
				pos += ret;
			else if (WSAGetLastError() != WSAEWOULDBLOCK)
				return -1;
		}
		return pos;
	}

	int Recv(void* buf, int len, bool waitall = false)
	{
		if (!waitall)
			return recv(m_hSocket, reinterpret_cast<char*>(buf), len, 0);
		int pos = 0;
		while (pos < len)
		{
			int ret = recv(m_hSocket, reinterpret_cast<char*>(buf) + pos, len - pos, 0);
			if (ret > 0)
				pos += ret;
			else if (WSAGetLastError() != WSAEWOULDBLOCK)
				return -1;
		}
		return pos;
	}

	int SendTo(const void* buf, int len, sockaddr* sa, int salen)
	{
		return sendto(m_hSocket, reinterpret_cast<const char*>(buf), len, 0, sa, salen);
	}

	int RecvFrom(void* buf, int len, sockaddr* sa, int* salen)
	{
		return recvfrom(m_hSocket, reinterpret_cast<char*>(buf), len, 0, sa, salen);
	}

	int PeekFrom(void* buf, int len, sockaddr* sa = 0, int* salen = 0)
	{
		int ret = -1;
		if (!sa || !salen)
			ret = recv(m_hSocket, reinterpret_cast<char*>(buf), len, MSG_PEEK);
		else recvfrom(m_hSocket, reinterpret_cast<char*>(buf), len, MSG_PEEK, sa, salen); 
		if (ret < len)
			return -1;
		else 
		{
			u_long readylen;
			if (SOCKET_ERROR == ioctlsocket(m_hSocket, FIONREAD, &readylen))
				return -1;
			// return the data_len already reached
			return readylen;
		}
	}

	int SendOOB(char ch, sockaddr* sa = 0, int salen = 0)
	{
		if (!sa || !salen)
			return send(m_hSocket, &ch, 1, MSG_OOB);
		return sendto(m_hSocket, &ch, 1, MSG_OOB, sa, salen); 
	}

	bool IOCtrl(long cmd, u_long* argp)
	{
		if (INVALID_SOCKET == m_hSocket)
			return false;
		// WSAEventSelect automatically set a socket to nonblocking mode.
		// then any attempt to set the socket back to blocking mode will fail
		return !ioctlsocket(m_hSocket, cmd, argp);
	}

protected:
	SOCKET m_hSocket;
};


class CEventSocket : public CBaseSocket, public threadbase
{
protected:
	// callbacks
	virtual void OnReceive(int nErrorCode){}
	virtual void OnSend(int nErrorCode){}
	virtual void OnOutOfBandData(int nErrorCode){}
	virtual void OnAccept(int nErrorCode){}
	virtual void OnConnect(int nErrorCode){}
	virtual void OnClose(int nErrorCode){}
	virtual void OnTimeout(int nElapse){}

public:
	CEventSocket(void)
		: CBaseSocket()
		, m_hEvent(WSA_INVALID_EVENT)
	{
		threadbase::init(false);
	}

	~CEventSocket(void)
	{
		Close();
		threadbase::destroy();
	}

	void SetTimeout(int timeout)
	{
		threadbase::settimeout(timeout);
	}

	virtual int Attach(SOCKET hSocket, long lEvent =
		FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE)
	{
		ASSERT(WSA_INVALID_EVENT == m_hEvent);
		m_hEvent = WSACreateEvent();
		WSAEventSelect(hSocket, m_hEvent, lEvent);
		int ret = __super::Attach(hSocket);
		if (threadbase::waitend(0))
			threadbase::init();
		threadbase::start();
		return ret;
	}

	virtual SOCKET Detach()
	{
		threadbase::pause();
		SOCKET hSocket = __super::Detach();
		WSAEventSelect(hSocket, WSA_INVALID_EVENT, 0);
		WSACloseEvent(m_hEvent);
		SetTimeout(INFINITE);
		return hSocket;
	}

protected:
	WSAEVENT m_hEvent;

private:
	virtual void idle()
	{
		OnTimeout(gettimeout());
	}

	virtual bool pump()
	{
		WSANETWORKEVENTS NetworkEvents;
		int err = WSAEnumNetworkEvents(m_hSocket, m_hEvent, &NetworkEvents);
		if (SOCKET_ERROR == err) 
			return false;
		if (NetworkEvents.lNetworkEvents & FD_READ)
			OnReceive(NetworkEvents.iErrorCode[FD_READ_BIT]);
		if (NetworkEvents.lNetworkEvents & FD_WRITE)
			OnSend(NetworkEvents.iErrorCode[FD_WRITE_BIT]);
		if (NetworkEvents.lNetworkEvents & FD_OOB)
			OnOutOfBandData(NetworkEvents.iErrorCode[FD_OOB_BIT]);
		if (NetworkEvents.lNetworkEvents & FD_ACCEPT)
			OnAccept(NetworkEvents.iErrorCode[FD_ACCEPT_BIT]);
		if (NetworkEvents.lNetworkEvents & FD_CONNECT)
			OnConnect(NetworkEvents.iErrorCode[FD_CONNECT_BIT]);
		if (NetworkEvents.lNetworkEvents & FD_CLOSE)
			OnClose(NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
		return true;
	}
};


class CAutoSocket : public CEventSocket
{
public:
	CAutoSocket(
		const char* ip = NULL, 
		unsigned short port = 0,
		int timeout = -1,
		uint64 timeoutLC = -1)
	{
		SetAddress(ip, port);
		SetTimeout(timeout);
		SetLCTimeout(timeoutLC);
		Reconnect();
	}

	void SetAddress(const char* ip, unsigned short port) 
	{
		if (ip && ip[0])
			strncpy_s(m_ip, 16, ip, 15);
		m_ip[15] = NULL;
		m_port = port;
	}

	void SetLCTimeout(uint64 timeout)
	{
		m_AccTimeOut = 0;
		m_LCTimeOut = timeout;
	}

	int Reconnect()
	{
		Close();
		return __super::Connect(m_ip, m_port);
	}

	virtual void OnReceive(int nErrorCode)
	{
		m_AccTimeOut = 0;
		__super::OnReceive(nErrorCode);
	}

	virtual void OnOutOfBandData(int nErrorCode)
	{
		m_AccTimeOut = 0;
		__super::OnOutOfBandData(nErrorCode);
	}

	virtual void OnTimeout(int nElapse)
	{
		if (nElapse > 0)
			m_AccTimeOut += nElapse;
		if (m_AccTimeOut >= m_LCTimeOut)
			Reconnect();
		__super::OnTimeout(nElapse);
	}

private:
	// disable connect which with explicit address. 
	int Connect(const char* RemoteIp, unsigned short RemotePort);

private:
	uint64 m_AccTimeOut;
	uint64 m_LCTimeOut;
	unsigned short m_port;
	char m_ip[16];
};


//neighbor in LAN 
class CNeighborhood : public cr::CEventSocket
{
public:
	const uint16 lan_port;

	struct NeighborInfo
	{
		uint32 ck;
		uint32 op;
		uint32 id;
		uint32 ip;
		inline uint32 chksum() { return id^ip^op; }
	};

public:
	CNeighborhood(uint16 port)
		: lan_port(port)
	{}

	virtual int Create(uint32 id, uint32 ip)
	{
		appid = id;
		appip = ip;

		int ret;
		SOCKET hSocket = socket(AF_INET, SOCK_DGRAM, 0);
		if (INVALID_SOCKET == hSocket)
			return WSAGetLastError();
		BOOL enable = TRUE;
		ret = setsockopt(hSocket, 
			SOL_SOCKET, SO_REUSEADDR, (char*)&enable, sizeof(enable));
		if (SOCKET_ERROR == ret)
			return WSAGetLastError();
		ret = setsockopt(hSocket, 
			SOL_SOCKET, SO_BROADCAST, (char*)&enable, sizeof(enable));
		if (SOCKET_ERROR == ret)
			return WSAGetLastError();
		sockaddr_in sin;
		sin.sin_family = AF_INET;
		sin.sin_port = htons(lan_port);
		sin.sin_addr.s_addr = INADDR_ANY;
		ret = bind(hSocket, (sockaddr*)&sin, sizeof(sin));
		if (SOCKET_ERROR == ret)
			return WSAGetLastError();
		return Attach(hSocket);
	}

	virtual void OnReceive(int nErrorCode)
	{
		if (nErrorCode)
			return;
		NeighborInfo info;
		sockaddr_in sin;
		int salen = sizeof(sin);
		int len = RecvFrom(&info, sizeof(info), (sockaddr*)&sin, &salen);
		if (len < 0 || (unsigned int)len < sizeof(info) 
			|| info.ck ^ info.chksum())
		{
			Close();
			Create(appid, appip);
			return;
		}
		if (info.id == appid)
			return;
		DEBUGING(printf("neighbor receive:id=%d, ip=%s, op=%d\n", info.id, 
			inet_ntoa(*(in_addr*)&info.ip), info.op));
		if (info.op == 0) { /*broadcast*/
			neighbors[info.id] = info.ip;
			Feedback((sockaddr*)&sin, sizeof(sin));
		}
		else if (info.op == 1) /*feedback*/
			neighbors[info.id] = info.ip;
		else if (info.op == -1) /*quit*/
			neighbors.erase(info.id);
	}

	int Broadcast(const void* buf, int len)
	{
		sockaddr_in sin;
		sin.sin_family = AF_INET;
		sin.sin_port = htons(lan_port);
		sin.sin_addr.s_addr = appip;
		sin.sin_addr.s_impno = 255; //broadcast in LAN Internal
		DEBUGING(printf("broadcast:ip=%s, port=%d\n",
			inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)));
		return 	SendTo(buf, len, (sockaddr*)&sin, sizeof(sin));
	}

	int Feedback(sockaddr* sa, int salen)
	{
		NeighborInfo info;
		info.id = appid;
		info.ip = appip;
		info.op = 1;
		info.ck = info.chksum();
		DEBUGING(printf("feedback to:ip=%s, port=%d\n", 
			inet_ntoa(((sockaddr_in*)sa)->sin_addr), ntohs(((sockaddr_in*)sa)->sin_port)));
		return SendTo(&info, sizeof(info), sa, salen);
	}

	int JoinNeighbor()
	{
		NeighborInfo info;
		info.id = appid;
		info.ip = appip;
		info.op = 0;
		info.ck = info.chksum();
		return Broadcast(&info, sizeof(info));
	}

	int QuitNeighbor()
	{
		NeighborInfo info;
		info.id = appid;
		info.ip = appip;
		info.op = -1;
		info.ck = info.chksum();
		return Broadcast(&info, sizeof(info));
	}

	bool FindNeighbor(uint32 uid, uint32& ip)
	{
		NeighborIter it = neighbors.find(uid);
		if (neighbors.end() == it)
			return false;
		ip = it->second;
		return true;
	}

protected:
	typedef std::map<uint32, uint32> NeighborMap;
	typedef NeighborMap::iterator NeighborIter;
	NeighborMap neighbors;

private:
	uint32 appid;
	uint32 appip;
};


template <class type_packhead = packhead0_t>
struct IHandlerBase : public sigslot::has_slots<>
{
	virtual void OnReceived(int nErrorCode, pack_t<type_packhead>* pack, int len) = 0;
	virtual void OnSent(int nErrorCode) = 0;
	virtual void OnOutOfBandData(int nErrorCode, char ch) = 0;
	virtual void OnAccepted(int nErrorCode, SOCKET s, sockaddr sa) = 0;
	virtual void OnConnected(int nErrorCode) = 0;
	virtual void OnClosed(int nErrorCode) = 0;
	virtual void OnTimeout(int nElapse) = 0;
};


template <class type_packhead = packhead0_t>
class CSigslotSocket : public CEventSocket
{
public:
	sigslot::signal3<int, pack_t<type_packhead>*, int> SignalReceived;
	sigslot::signal1<int> SignalSent;
	sigslot::signal2<int, char> SignalOutOfBandData;
	sigslot::signal3<int, SOCKET, sockaddr> SignalAccepted;
	sigslot::signal1<int> SignalConnected;
	sigslot::signal1<int> SignalClosed;
	sigslot::signal1<int> SignalTimeout;

public:
	enum { UNEXPECTED_SIZE = 1024*1024*16 }; // never beyond

protected:
	virtual void OnReceive(int nErrorCode)
	{
		if (0 != nErrorCode) {
			SignalReceived(nErrorCode, 0, 0);
			return;
		}

		type_packhead head;
		int len = PeekFrom(&head, sizeof(head));
		if ((int)len < (int)sizeof(head))  //notice: signed -1 is unsigned 0xffffffff
			return; //ignore if no enough data
		else if (head.len < sizeof(head) || head.len >= UNEXPECTED_SIZE) {
			SignalReceived(-1, 0, 0);
			return;
		}
		else if ((int)len < (int)head.len) //do it after head.len be verified above
			return; //ignore if no enough data
		else {
			pack_t<type_packhead>* pack = (pack_t<type_packhead>*) new char[head.len];
			//think about the case a big package beyond the socket buffer
			//you must wait all data that the data would be filled by N times
			//while wait all data, it may be block FOREVER with an error head.len
			nErrorCode = (Recv(pack, head.len, true) >= (int)head.len) ? 0 : -1;
			SignalReceived(nErrorCode, pack, len);
			delete pack;
		}
	}

	virtual void OnSend(int nErrorCode)
	{
		SignalSent(nErrorCode);
	}

	virtual void OnOutOfBandData(int nErrorCode)
	{
		char ch = 0;
		if (0 == nErrorCode)
			nErrorCode = (recv(m_hSocket, &ch, 1, MSG_OOB) > 0) ? 0 : -1;
		SignalOutOfBandData(nErrorCode, ch);
	}

	virtual void OnAccept(int nErrorCode)
	{
		CEventSocket sAccept;
		sockaddr sa;
		int len = sizeof(sa);
		if (0 == nErrorCode)
			nErrorCode = Accept(sAccept, &sa, &len);
		SignalAccepted(nErrorCode, sAccept.Detach(), sa);
	}

	virtual void OnConnect(int nErrorCode)
	{
		SignalConnected(nErrorCode);
	}

	virtual void OnClose(int nErrorCode)
	{
		SignalClosed(nErrorCode);
	}

	virtual void OnTimeout(int nElapse)
	{
		SignalTimeout(nElapse);
	}
};


//////////////////////////////////////////////////////////////////////////
// resolve domain name

struct __ns_resolve__
{
	static DWORD WINAPI thd(LPVOID pParam)
	{
		__ns_resolve__* info = (__ns_resolve__*)pParam;
		ASSERT(info && info->name_);
		hostent* hent = ::gethostbyname(info->name_);
		if (!hent)
			return 0;
		info->addr_ = *(IN_ADDR*)hent->h_addr;
		info->bresolved_ = true;
		return 0;
	}
	bool resolve(const char* name, DWORD timeout = -1)
	{
		name_ = name;
		bresolved_ = false;
		HANDLE hThread = CreateThread(0, 0, thd, this, 0, 0);
		if (WAIT_TIMEOUT == WaitForSingleObject(hThread, timeout))
			TerminateThread(hThread, 0);
		CloseHandle(hThread);
		return bresolved_;
	}
	const char* name_;
	IN_ADDR addr_;
	bool bresolved_;
};

const GUID guidAcceptEx = WSAID_ACCEPTEX;
const GUID guidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
const GUID guidConnectEx = WSAID_CONNECTEX;
const GUID guidDisconnectEx = WSAID_DISCONNECTEX;
const GUID guidTransmitFile = WSAID_TRANSMITFILE;
const GUID guidTransmitPacket = WSAID_TRANSMITPACKETS;
//const GUID guidWsaRecvMsg = WSAID_WSARECVMSG;
//const GUID guidWsaSendMsg = WSAID_WSASENDMSG;
//const GUID guidWsaPoll = WSAID_WSAPOLL; //vista later

struct __wsa_ex_functions__ 
{
	const LPFN_ACCEPTEX CR_AcceptEx;
	const LPFN_GETACCEPTEXSOCKADDRS CR_GetAcceptExSockaddrs;
	const LPFN_CONNECTEX CR_ConnectEx;
	const LPFN_DISCONNECTEX CR_DisconnectEx;
	const LPFN_TRANSMITFILE CR_TransmitFile;
	const LPFN_TRANSMITPACKETS CR_TransmitPacket;
	//const LPFN_WSARECVMSG CR_WsaRecvMsg;
	//const LPFN_WSASENDMSG CR_WsaSendMsg;
	//const LPFN_WSAPOLL CR_WsaPoll; //vista later

	__wsa_ex_functions__(int af = AF_INET)
		: CR_AcceptEx(NULL)
		, CR_GetAcceptExSockaddrs(NULL)
		, CR_ConnectEx(NULL)
		, CR_DisconnectEx(NULL)
		, CR_TransmitFile(NULL)
		, CR_TransmitPacket(NULL)
		//, CR_WsaRecvMsg(NULL)
		//, CR_WsaSendMsg(NULL)
		//, CR_WsaPoll(NULL)
	{
		SOCKET s = socket(af, SOCK_STREAM, IPPROTO_TCP);
		if (INVALID_SOCKET == s)
			PROMPT(_T("please use __init_sock__ before. "));
		DWORD dwBytes = 0;

		ASSERT(!CR_AcceptEx);
		LPFN_ACCEPTEX lpfnAcceptEx = NULL;
		WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
			(LPVOID)&guidAcceptEx, sizeof(GUID), 
			(LPVOID)&lpfnAcceptEx, sizeof(LPVOID),
			&dwBytes, 0, 0);
		*const_cast<LPFN_ACCEPTEX*>(&CR_AcceptEx) = lpfnAcceptEx;
		ASSERT(CR_AcceptEx);

		ASSERT(!CR_GetAcceptExSockaddrs);
		LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs = NULL;
		WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
			(LPVOID)&guidGetAcceptExSockaddrs, sizeof(GUID), 
			(LPVOID)&lpfnGetAcceptExSockaddrs, sizeof(LPVOID),
			&dwBytes, 0, 0);
		*const_cast<LPFN_GETACCEPTEXSOCKADDRS*>(&CR_GetAcceptExSockaddrs) = lpfnGetAcceptExSockaddrs;
		ASSERT(CR_GetAcceptExSockaddrs);

		ASSERT(!CR_ConnectEx);
		LPFN_CONNECTEX lpfnConnectEx = NULL;
		WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
			(LPVOID)&guidConnectEx, sizeof(GUID), 
			(LPVOID)&lpfnConnectEx, sizeof(LPVOID),
			&dwBytes, 0, 0);
		*const_cast<LPFN_CONNECTEX*>(&CR_ConnectEx) = lpfnConnectEx;
		ASSERT(CR_ConnectEx);

		ASSERT(!CR_DisconnectEx);
		LPFN_DISCONNECTEX lpfnDisconnectEx = NULL;
		WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
			(LPVOID)&guidDisconnectEx, sizeof(GUID), 
			(LPVOID)&lpfnDisconnectEx, sizeof(LPVOID),
			&dwBytes, 0, 0);
		*const_cast<LPFN_DISCONNECTEX*>(&CR_DisconnectEx) = lpfnDisconnectEx;
		ASSERT(CR_DisconnectEx);

		ASSERT(!CR_TransmitFile);
		LPFN_TRANSMITFILE lpfnTransmitFile = NULL;
		WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
			(LPVOID)&guidTransmitFile, sizeof(GUID), 
			(LPVOID)&lpfnTransmitFile, sizeof(LPVOID),
			&dwBytes, 0, 0);
		*const_cast<LPFN_TRANSMITFILE*>(&CR_TransmitFile) = lpfnTransmitFile;
		ASSERT(CR_TransmitFile);

		ASSERT(!CR_TransmitPacket);
		LPFN_TRANSMITPACKETS lpfnTransmitPacket = NULL;
		WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
			(LPVOID)&guidTransmitPacket, sizeof(GUID), 
			(LPVOID)&lpfnTransmitPacket, sizeof(LPVOID),
			&dwBytes, 0, 0);
		*const_cast<LPFN_TRANSMITPACKETS*>(&CR_TransmitPacket) = lpfnTransmitPacket;
		ASSERT(CR_TransmitPacket);

		//ASSERT(!CR_WsaRecvMsg);
		//LPFN_WSARECVMSG lpfnWsaRecvMsg = NULL;
		//WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
		//	(LPVOID)&guidWsaRecvMsg, sizeof(GUID), 
		//	(LPVOID)&lpfnWsaRecvMsg, sizeof(LPVOID),
		//	&dwBytes, 0, 0);
		//*const_cast<LPFN_WSARECVMSG*>(&CR_WsaRecvMsg) = lpfnWsaRecvMsg;
		//ASSERT(CR_WsaRecvMsg);

		//ASSERT(!CR_WsaSendMsg);
		//LPFN_WSASENDMSG lpfnWsaSendMsg = NULL;
		//WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
		//	(LPVOID)&guidWsaSendMsg, sizeof(GUID), 
		//	(LPVOID)&lpfnWsaSendMsg, sizeof(LPVOID),
		//	&dwBytes, 0, 0);
		//*const_cast<LPFN_WSASENDMSG*>(&CR_WsaSendMsg) = lpfnWsaSendMsg;
		//ASSERT(CR_WsaSendMsg);

		//ASSERT(!CR_WsaPoll);
		//LPFN_WSAPOLL lpfnWsaPoll = NULL; //vista later
		//WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, 
		//	(LPVOID)&guidWsaPoll, sizeof(GUID), 
		//	(LPVOID)&lpfnWsaPoll, sizeof(LPVOID),
		//	&dwBytes, 0, 0);
		//*const_cast<LPFN_WSAPOLL*>(&CR_WsaPoll) = lpfnWsaPoll;
		//ASSERT(CR_WsaPoll); //vista later

		closesocket(s);
	}
};

}//namespace 

#endif //__CR_EVENT_SOCKET_H__